/*
Copyright 2024.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package controller

import (
	"context"
	"time"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"

	hostv1beta1 "gitlab.com/go-course-project/go15/skills/operator/myoperator/api/v1beta1"
)

// VirtrualMachineReconciler reconciles a VirtrualMachine object
type VirtrualMachineReconciler struct {
	client.Client
	Scheme *runtime.Scheme
}

// +kubebuilder:rbac:groups=host.go15,resources=virtrualmachines,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=host.go15,resources=virtrualmachines/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=host.go15,resources=virtrualmachines/finalizers,verbs=update

// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the VirtrualMachine object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile
func (r *VirtrualMachineReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
	l := log.FromContext(ctx, "namespace", req.Namespace)

	// TODO(user): your logic here
	// 获取当前这个对象
	obj := &hostv1beta1.VirtrualMachine{}
	if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
		if apierrors.IsNotFound(err) {
			// 说明这个资源已经被删除
			l.Info("%s not found", "namespace", req.NamespacedName)
		}
		return ctrl.Result{}, nil
	}

	// 判断状态有没有达到预期
	// 1. 达到预期，直接忽略
	l.Info("获取到资源", "name", obj.Name)

	switch obj.Status.Stage {
	case hostv1beta1.STAGE_CREATE_FAILED, hostv1beta1.STAGE_RUNNING:
		return ctrl.Result{}, nil
	case hostv1beta1.STAGE_CREATING:
		// 同步一下状态, 看有没有创建完成, 没完成5秒后再重试
		return ctrl.Result{Requeue: true, RequeueAfter: 5 * time.Second}, nil
	case hostv1beta1.STAGE_PADDING, "":
		// 2. 没达到预期--> 掉API, 等。。。 达到预期
		// 处理完成功, 更新Api Object的状态
		l.Info("%s 调用KVM API 进行资源创建中 ...", "name", obj.Name)

		obj.Status.Stage = hostv1beta1.STAGE_RUNNING
		if err := r.Status().Update(ctx, obj); err != nil {
			l.Error(err, "更新对象状态失败")
		}
	default:
		return ctrl.Result{}, apierrors.NewBadRequest("状态未识别: " + string(obj.Status.Stage))
	}

	return ctrl.Result{}, nil
}

// SetupWithManager sets up the controller with the Manager.
func (r *VirtrualMachineReconciler) SetupWithManager(mgr ctrl.Manager) error {
	return ctrl.NewControllerManagedBy(mgr).
		For(&hostv1beta1.VirtrualMachine{}).
		Complete(r)
}
